using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;

namespace Windy.Json
{
    public static class JsonDomManager
    {
        public static dynamic ToDom(this IEnumerable<JsonNotation> notations)
        {
            if (notations == null)
                return null;

            var context = notations.GetEnumerator();

            return ReadNotation(context);
        }

        private static dynamic ReadNotation(IEnumerator<JsonNotation> context, bool moveNext = true)
        {
            if (moveNext)
                NextNotation(context);

            switch (context.Current.Type)
            {
                case JsonNotationType.Undefined:
                case JsonNotationType.String:
                case JsonNotationType.Number:
                    return context.Current.Value;
                case JsonNotationType.True:
                    return true;
                case JsonNotationType.False:
                    return false;
                case JsonNotationType.Null:
                    return null;
                case JsonNotationType.ObjectBegin:
                    return ReadObject(context);
                case JsonNotationType.ArrayBegin:
                    return ReadArray(context);
                case JsonNotationType.ObjectEnd:
                case JsonNotationType.ArrayEnd:
                case JsonNotationType.NameSeperator:
                case JsonNotationType.MemberSeperator:
                default:
                    throw new JsonDomException("Unexpected Notation", context.Current);
            }
        }

        private static dynamic ReadObject(IEnumerator<JsonNotation> context)
        {
            var result = new ExpandoObject();
            var dictView = result as IDictionary<string, object>;

            NextNotation(context);

            while (context.Current.Type != JsonNotationType.ObjectEnd)
            {
                string key;

                switch (context.Current.Type)
                {
                    case JsonNotationType.Undefined:
                    case JsonNotationType.String:
                        key = context.Current.Value;
                        break;
                    default:
                        throw new JsonDomException("Unexpected Object Content", context.Current);
                }

                ExpectNotation(context, JsonNotationType.NameSeperator);

                dynamic value = ReadNotation(context);
                dictView.Add(key, value);

                NextNotation(context);

                switch (context.Current.Type)
                {
                    case JsonNotationType.ObjectEnd:
                        // Ignore
                        break;
                    case JsonNotationType.MemberSeperator:
                        NextNotation(context);
                        break;
                    default:
                        throw new JsonDomException("Unexpected Notation", context.Current);
                }
            }

            return result;
        }

        private static dynamic ReadArray(IEnumerator<JsonNotation> context)
        {
            var result = new List<dynamic>();

            NextNotation(context);

            while (context.Current.Type != JsonNotationType.ArrayEnd)
            {
                dynamic value = ReadNotation(context, false);
                result.Add(value);

                NextNotation(context);

                switch (context.Current.Type)
                {
                    case JsonNotationType.ArrayEnd:
                        // Ignore
                        break;
                    case JsonNotationType.MemberSeperator:
                        NextNotation(context);
                        break;
                    default:
                        throw new JsonDomException("Unexpected Notation", context.Current);
                }
            }

            return result;
        }

        private static void NextNotation(IEnumerator<JsonNotation> context)
        {
            if (!context.MoveNext())
                throw new JsonDomException("Unexpected end of context");
        }

        private static void ExpectNotation(IEnumerator<JsonNotation> context, JsonNotationType typeExpected)
        {
            if (!context.MoveNext())
                throw new JsonDomException("Unexpected end of context");

            if (context.Current.Type != typeExpected)
                throw new JsonDomException("Unexpected notation", context.Current);
        }

    }
}
